#include <fcntl.h>
#include <unistd.h>

#include <common/filelock.h>
#include <common/io.h>

/** 
* @brief Set a lock of the given type.
* 
* @param io
* @param start
* @param len
* @param lock_type Lock type, as defined by fcntl() manpage.
* 
* @return The return value of fcntl() call.
*/
API_FILELOCAL int filelock_set_lock(iohandle_t io, off_t start, off_t len, short lock_type) {
	struct flock lock;

	lock.l_type = lock_type;
	lock.l_whence = SEEK_SET;
	lock.l_start = start;
	lock.l_len = len;
	
	return fcntl(fileno((FILE*)io), F_SETLKW, &lock);
}

/** 
* @brief Get lock status. 
* 
* @param io
* @param start
* @param len
* @param lock_type
* 
* @return Zero if the lock is not set, 1 if the lock is set, -1 if error.
*/
API_FILELOCAL int filelock_get_lock(iohandle_t io, off_t start, off_t len, short lock_type) {
	int rv;
	struct flock lock;

	lock.l_type = lock_type;
	lock.l_whence = SEEK_SET;
	lock.l_start = start;
	lock.l_len = len;
	
	lock.l_pid = 0;

	rv = fcntl(fileno((FILE*)io), F_GETLK, &lock);
	if (rv != 0)
		return -1;
	return (lock.l_pid != 0 ? 1 : 0);
}

/** 
* @brief Set a writelock
* 
* @param io
* @param start
* @param len
* 
* @return Return value of filelock_set_lock
*/
API_LIBLOCAL int filelock_writelock(iohandle_t io, off_t start, off_t len) {
	return filelock_set_lock(io, start, len, F_WRLCK);
}

/** 
* @brief Set a readlock
* 
* @param io
* @param start
* @param len
* 
* @return Return value of filelock_set_lock
*/
API_LIBLOCAL int filelock_readlock(iohandle_t io, off_t start, off_t len) {
	return filelock_set_lock(io, start, len, F_RDLCK);
}

/** 
* @brief Unset any kind of lock
* 
* @param io
* @param start
* @param len
* 
* @return Return value of filelock_set_lock
*/
API_LIBLOCAL int filelock_unlock(iohandle_t io, off_t start, off_t len) {
	return filelock_set_lock(io, start, len, F_UNLCK);
}

/** 
* @brief Verify lock status
* 
* @param io
* @param start
* @param len
* 
* @return Return value of filelock_get_lock
*/
API_LIBLOCAL int filelock_checklock(iohandle_t io, off_t start, off_t len) {
	return filelock_get_lock(io, start, len, F_WRLCK);
}
